﻿using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using SharpDX;
using SharpDX.XAPO;
using SynergyLib.DSP;
using SynergyLib.SMath;

namespace XAudio2Controls
{
    [StructLayout(LayoutKind.Sequential)]
    public struct ChorusParams
    {
        internal float DelayMS { get; set; }
        internal float Feedback { get; set; }
        internal float Rate { get; set; }
        internal int Voices { get; set; }
    }
    /// <summary>
    /// A Flanger effect
    /// </summary>
    public class ChorusEffect : AudioProcessorBase<ChorusParams>
    {
        #region fields

        private Object o = new Object();
        #endregion fields

        #region Properties

        private float max_time_delay
        {
            get { return Parameters.DelayMS; }
        }
        private float amp
        {
            get { return Parameters.Feedback; }
        }
        #endregion Properties

        public override void Process(BufferParameters[] inputProcessParameters, BufferParameters[] outputProcessParameters, bool isEnabled)
        {
            int frameCount = inputProcessParameters[0].ValidFrameCount;
            DataStream input = new DataStream(inputProcessParameters[0].Buffer, frameCount * InputFormatLocked.BlockAlign, true, true);
            DataStream Output = new DataStream(inputProcessParameters[0].Buffer, frameCount * InputFormatLocked.BlockAlign, true, true);
            float[] leftX = new float[frameCount];
            float[] rightX = new float[frameCount];
            //leftX[0] = input.Read<float>();
            //rightX[0] = input.Read<float>();
            //leftX[0] = 0;
            //rightX[0] = 0;
            for (int n = 0; n < frameCount; n++, Counter++)
            {
                leftX[n] = input.Read<float>();
                rightX[n] = input.Read<float>();
                // Output.Write(leftX[n] + GetDelay(leftX[n], Parameters.Voices));
                // Output.Write(rightX[n] + GetDelay(rightX[n], Parameters.Voices));
                Output.Write((amp) * leftX[n] + GetSingleDelayLine(rightX[n]));
                Output.Write((amp) * rightX[n] + GetSingleDelayLine(leftX[n]));

            }
        }

        #region Private Methods

        /// <summary>
        /// applies a delay to the provided input and returns multiple copies of it.
        /// </summary>
        /// <param name="input"></param>
        /// <param name="voices"></param>
        /// <returns></returns>
        private float GetDelay(float input, int voices)
        {
            float delayLine = new float();
            for (int i = 0; i < voices; i++)
            {
                delayLine += amp * input - (amp * cur_delay());
            }
            return delayLine;
        }

        private float GetSingleDelayLine(float input)
        {
            return input - (amp * cur_delay());
        }
        /// <summary>
        /// Sine reference to create oscillating delay
        /// </summary>
        private float sin_ref()
        {
            // if (index % 2 == 0)
            // {
            //    index *= -1;
            //  }
            return (float)Math.Sin(Trig.TwoPI * Counter * (Parameters.Rate / InputFormatLocked.SampleRate));
        }
        private float cur_sin()
        {
            return (float)(sin_ref());
        }
        /// <summary>
        /// generate delay from 1 - max_samp_delay and ensure whole number
        /// </summary>
        /// <returns></returns>
        private float cur_delay()
        {
            return (float)(cur_sin() * (1 - (max_time_delay)));
        }
        #endregion Private Methods
    }
}